package IA;

import Controle.TelaJogo;
import Controle.TelaNivelJogada;
import Jplay.Scene;
import MotorGrafico.MotorGrafico;
import MotorGrafico.Tabuleiro;
import java.util.ArrayList;
import MotorRegras.Regras;
import MotorGrafico.Indice;
import java.util.Random;

import util.Constantes;

public class AgenteSmith {

	Tabuleiro tabuleiro;
	Regras regras;
	int[][] pieces;
	boolean kamikaze = false;

	public AgenteSmith(Tabuleiro tab, Regras regras) {
		this.tabuleiro = tab;
		this.regras = regras;
		this.pieces = tabuleiro.getTileMatrix();
	}

	public void play() {
		System.gc();
		this.buildTree(this.tabuleiro);
	}

	private int heuristica1(Tabuleiro tab) {

		int[][] posicoes = tab.getTileMatrix();

		int blackPoints = 0;
		int whitePoints = 0;

		int meio = (int) Math.sqrt((3.5*3.5)+(3.5*3.5));
		
		for (int i = 0; i < posicoes.length; i++) {
			for (int j = 0; j < posicoes.length; j++) {
				
				switch (posicoes[i][j]) {
				case 1:// pontos do bispo preto
					blackPoints += 12;
					break;
				case 2:// pontos do cavalo preto
					blackPoints += 12;
					break;
				case 3:// pontos do peao preto
					blackPoints += 5;
					break;
				case 4:// pontos da rainha preto
					blackPoints += 60;
					break;
				case 5:// pontos do rei preto
					blackPoints += 100;
					break;
				case 6:// pontos da torre preto
					blackPoints += 16;
					break;
				case 7:// pontos do bispo branco
					whitePoints += 12;
					break;
				case 8:// pontos do cavalo branco
					whitePoints += 12;
					break;
				case 9:// pontos do peao branco
					whitePoints += 5;
					break;
				case 10:// pontos da rainha branco
					whitePoints += 60;
					break;
				case 11:// pontos do rei branco
					whitePoints += 100;
					break;
				case 12:// pontos da torre branco
					whitePoints += 16;
					break;
				}
			}
		}

		return  whitePoints-blackPoints;
		
	}

	/*
	 * private int heuristica2(Tabuleiro tab) {
	 * 
	 * return 0; }
	 */

	private void buildTree(Tabuleiro tabuleiro) {

		int profundidade = TelaNivelJogada.nivelEscolhido;

		System.out.println(TelaNivelJogada.nivelEscolhido);
		NodeOfState node = new NodeOfState(tabuleiro, /*null,*/ null, null,null,profundidade);

		boolean testeminimax, podaalfabeta;
		
		testeminimax = true;
		podaalfabeta = false;
		
		// dependendo da escolha da cor do jogador, deverá ser passado true ou
		// false.
		// se Agente = peças pretas, passar false, se não, passar true

		// this.setFilhos(profundidade, node, true, 100, -100);

		if (testeminimax){
			Tabuleiro next = minimax(node,profundidade);
			
			for(int a = 0; a < next.getTileMatrix().length; a++){
				for(int b = 0; b < next.getTileMatrix().length; b++){
					if (next.getTile(a, b).id != node.getTabuleiro().getTile(a,b).id){
						
                    if (Tabuleiro.isPecaNegra( node.getTabuleiro().getPeca(a, b)) ){
                            TelaJogo tj = (TelaJogo) MotorGrafico.getInstance().getTela(Constantes.TELA_JOGAR);
                            tj.getEstado().pecaComida( new Indice(a, b));
                    	}						
						node.getTabuleiro().setPeca(next.getTile(a, b).id, a, b);
						
						TelaJogo tj = (TelaJogo) MotorGrafico.getInstance().getTela(Constantes.TELA_JOGAR);
						tj.getEstado().setJogadorDaVez(Constantes.JOGADOR_02);
						tj.getEstado().setHabilitouRelogio(false);
						
					}
				}
			}
			
		} else if (podaalfabeta){
			System.out.println(this.alfabeta(node, profundidade, Integer.MIN_VALUE,	Integer.MAX_VALUE, true));
	/*
			NodeOfState proximaJogada = node.getChildren().get(0);
			Integer aux = node.getChildren().get(0).alfa;
			for (NodeOfState child : node.getChildren()) {
				if (child.alfa > aux) {
					aux = child.alfa;
					proximaJogada = child;
				}
				System.out.println();
				child.getTabuleiro().imprIndice();
				child.printValores();
			}
	
			int tipo = this.tabuleiro.getPeca(proximaJogada.origem.i,proximaJogada.origem.j);
			this.tabuleiro.removerPeca(proximaJogada.origem.i,proximaJogada.origem.j);
			this.tabuleiro.setPeca(tipo, proximaJogada.alvo.i, proximaJogada.alvo.j);
			*/
		}
	}

	public ArrayList<NodeOfState> sucessores(NodeOfState parent, boolean turn) {

		Tabuleiro tab = parent.getTabuleiro();
		
		ArrayList<NodeOfState> sucessores = new ArrayList<NodeOfState>();
		
		int[][] pecas = tab.getTileMatrix();

		for (int i = 0; i < pecas.length; i++) {
			for (int j = 0; j < pecas.length; j++) {

				if (!tab.isPosicaoVazia(i, j)) {
					Indice origem = new Indice(i, j);
					if (turn) {
						if (tab.getTile(i, j).id > 6) { // peças brancas
							for (int a = 0; a < pecas.length; a++) {
								for (int b = 0; b < pecas.length; b++) {
									Indice alvo = new Indice(a, b);
									if ((tab.getPeca(alvo.i, alvo.j) <= 6) || (tab.getPeca(alvo.i, alvo.j) == 0)){
										if (regras.isMovimentoValido(tab, origem,alvo, kamikaze))   
										{
											Tabuleiro newTab = new Tabuleiro();
											newTab = tab.clone();
											newTab.removerPeca(origem.i, origem.j);
											newTab.setPeca(tab.getTile(i, j).id,alvo.i, alvo.j);
											NodeOfState newNode = new NodeOfState(newTab, /*null,*/ origem, alvo,parent, parent.getDepth()-1);
											sucessores.add(newNode);
										}
									}
								}
							}
						}
					} else {
						if (tab.getTile(i, j).id <= 6) { // peças pretas
							for (int a = 0; a < pecas.length; a++) {
								for (int b = 0; b < pecas.length; b++) {
									Indice alvo = new Indice(a, b);
									if ((tab.getPeca(alvo.i, alvo.j) > 6) || (tab.getPeca(alvo.i, alvo.j) == 0)){
										if (regras.isMovimentoValido(tab, origem,alvo, kamikaze)) 
										{
											Tabuleiro newTab = new Tabuleiro();
											newTab = tab.clone();
											newTab.removerPeca(origem.i, origem.j);
											newTab.setPeca(tab.getTile(i, j).id,alvo.i, alvo.j);
											NodeOfState newNode = new NodeOfState(newTab, /*null,*/ origem, alvo,parent,parent.getDepth()-1 );
											sucessores.add(newNode);
										}
									}
								}
							}
						}
					}
				}
			}
		}
				
		return sucessores;
	}

	/*
	 * private void setFilhos(int profundidade, NodeOfState nodeOfState, boolean
	 * turn, int alfa, int beta) {
	 * 
	 * nodeOfState.setChildren(sucessores(nodeOfState.getTabuleiro(), turn,
	 * alfa, beta));
	 * 
	 * if (profundidade > 0){ for (NodeOfState n : nodeOfState.getChildren()) {
	 * setFilhos(profundidade - 1, n, !turn, alfa, beta); } } }
	 */
	private NodeOfState min(NodeOfState a, NodeOfState b) {
		if (a.getValor() > b.getValor()){
			return b;
		}
		else{
			return a;
		}		
	}

	private NodeOfState max(NodeOfState a, NodeOfState b) {
		if (a.getValor() > b.getValor()) {
			return a;
		}
		else {
			return b;
		}
	}

	private Integer alfabeta(NodeOfState no, int profundidade, int alfa,int beta, boolean turn) {

		if (profundidade == 0) {
			//System.out.println("Entrou");
			return heuristica1(no.getTabuleiro());
		}

		if (turn) {
			//no.setChildren(sucessores(no, turn));
			for (NodeOfState child : sucessores(no, turn)) {
				//alfa = max(alfa,alfabeta(child, profundidade - 1,alfa,beta, !turn));
				if (beta <= alfa) {
					break;
				}
			}
			no.alfa = alfa;
			no.beta = beta;
			return alfa;
		} else {
			//no.setChildren(sucessores(no, turn));
			for (NodeOfState child : sucessores(no, turn)) {
				//beta = min(beta,alfabeta(child, profundidade - 1, alfa, beta,!turn));
				if (beta <= alfa) {
					break;
				}
			}
			no.alfa = alfa;
			no.beta = beta;
			return beta;
		}
		
	}
	
	public Tabuleiro minimax(NodeOfState atual,int profundidade){
		
		NodeOfState maxState = maxvalue(atual,profundidade);
		
		if (maxState == atual){
			System.out.println("Iguais.");
			System.exit(0);
		}
		
		System.out.println("Profundidade:"+maxState.getDepth());
		
		while(maxState.getDepth() != TelaNivelJogada.nivelEscolhido-1){ //profundidade - 1
			maxState = maxState.getParent();
		}
		
		System.out.println("Profundidade:"+maxState.getDepth());
		
		//maxState é o nó da melhor jogada.
		int v = maxState.getValor();
		System.out.println("Valor: "+v);
		//System.out.println("Filhos:"+atual.getChildren().size());

		//ArrayList<NodeOfState> candidatos = new ArrayList<NodeOfState>();
		
		/*for(NodeOfState n:maxState.getChildren()){
			if (v == maxState.getValor()){
				candidatos.add(n);
			}
		}
		
		Random gen = new Random();
		*/		
		//return candidatos.get(gen.nextInt(candidatos.size()-1)).getTabuleiro();
		return maxState.getTabuleiro();
	}
	
	public NodeOfState maxvalue(NodeOfState node, int profundidade){
		
		if(profundidade == 0){
			node.setValor(heuristica1(node.getTabuleiro()));
			return node;
		}
		
		NodeOfState v = new NodeOfState(Integer.MIN_VALUE);
		
		//node.setChildren();
		for(NodeOfState n : sucessores(node,true)){
			v = max(v,minvalue(n,profundidade-1));
		}
		
		return v;
		
	}
	
	public NodeOfState minvalue(NodeOfState node, int profundidade){
		
		if(profundidade == 0){
			node.setValor(heuristica1(node.getTabuleiro()));
			return node;
		}
		
		NodeOfState v = new NodeOfState(Integer.MAX_VALUE);
		//int v = Integer.MAX_VALUE;
		//node.setChildren();
		for(NodeOfState n :sucessores(node,false)){
			v = min(v,maxvalue(n,profundidade-1));
		}
		
		//node.setValor(v);
		
		return v;
	}
}
